package com.hzdaba.aiapi.runnable;

import com.hzdaba.aiapi.domain.AiNumList;
import com.hzdaba.aiapi.exception.NumImportException;
import com.hzdaba.aiapi.mapper.AiNumListMapper;
import lombok.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

/**
 * 处理导入号码的任务
 * 如果把这个类型的 Bean 放入 Spring 的 IOC 容器中，那么 bean 的作用范围就要设置成 prototype
 * 作用域为 singleton 的对象要注入作用域为 prototype 的对象会出现问题
 * 这样反过来会不会有问题我不确定
 * 如果不将 Bean 交给 Spring 管理，那么 Spring 的 @Transactional 注解就无效，所以一次只能执行一条 SQL
 *
 * @author 刘逸晖
 */
@Component
//用 Cglib 代理似乎无法为这个对象设置属性
//@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.DEFAULT)
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString
@Builder
@Getter
@Setter
public class NumsImportRunnable implements Runnable {

    /**
     * 访问号码数据
     */
    @Autowired
    private AiNumListMapper aiNumListMapper;

    /**
     * 访问 Redis
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 缓存每批号码成功导入数的 key
     * 这个名字实在不好取
     */
    @Value("${aiapi.ainumlist.import.batches.success.count.cache.key}")
    private String keyOfSuccess;

    /**
     * 每批号码成功导入数的过期时间，单位分钟
     */
    @Value("${aiapi.ainumlist.import.batches.success.count.cache.expiry}")
    private int expiryOfSuccess;

    /**
     * 每次导入号码的唯一键
     */
    private String uniqueKey;

    /**
     * 号码列表
     * 每次只导入一部分
     */
    private List<AiNumList> aiNumList;

    /**
     * 起始索引（包含）
     */
    private int begin;

    /**
     * 结束索引（包含）
     */
    private int end;

    /**
     * 日志记录器
     */
    private static final Logger logger = LoggerFactory.getLogger(NumsImportRunnable.class);

    @Override
    @Transactional(timeout = 1)
    public void run() {
        try {
            //导入号码
            this.aiNumListMapper.insertAiNumList(aiNumList, begin, end);

//累加本批次导入的成功数
            this.redisTemplate.opsForHash().increment(this.keyOfSuccess, uniqueKey, (this.end - this.begin) + 1);

            if (ThreadLocalRandom.current().nextInt(100) == 0) {//随机数是0，要设置过期时间
                this.redisTemplate.expire(this.keyOfSuccess, this.expiryOfSuccess, TimeUnit.MINUTES);
            }

//            StringBuilder log = new StringBuilder("批量导入号码成功。")
//                    .append("批次 id + 请求 id：")
//                    .append(this.uniqueKey)
//                    .append("；")
//                    .append("起始索引：")
//                    .append(this.begin)
//                    .append("；")
//                    .append("结束索引：")
//                    .append(this.end)
//                    .append("。");
//            logger.info(log.toString());
        } catch (Exception e) {
//            StringBuilder log = new StringBuilder("批量导入号码失败。")
//                    .append("批次 id + 请求 id：")
//                    .append(this.uniqueKey)
//                    .append("；")
//                    .append("起始索引：")
//                    .append(this.begin)
//                    .append("；")
//                    .append("结束索引：")
//                    .append(this.end)
//                    .append("。");
//            logger.info(log.toString());

//            e.printStackTrace();
            //这个异常不能打印，我也不知道企业里是怎么解决的
            throw new NumImportException("导入号码失败，唯一键：" + this.uniqueKey);
        }
    }

}
